import os
import sys
import stat
import StatStruct
import Verbosity

class PathDataStorage:
	"""Holds dicts, lists, str, ints, longs, aso. They can be accessed like a
	normal dict/list-access, like, for example, a["key"], or set like a["key"]=value. It's
	designed to access those values via a path-like key-string.

	For example:
		Type 1: a["key1"]["key2"]["key3"]
		Type 2: a["/key1/key2/key3"]
	"""

	TYPE_OF_DIR="dir"
	TYPE_OF_LIST="list"
	TYPE_OF_FILE="file"

	def __init__(self, data={}):
		"""Constructor to initialize the object.

		Parameter:
			mixed data "data" is the data which is to be hold, can be from type dict, list, PathData, str,
				int, long, float, bool, None, unicode(-string), raises a TypeError if it's not of this type, required

			string pathSeparator Defines the string the key-path-type is defined with, like "a/b/c", default os.sep, optional
		"""

		self.verbosity=Verbosity.Verbosity(False, "* "+str(self.__class__)+",")

		# Store blocksize of operating system
		self.__statfsBlocksize=os.statvfs(".").f_bsize

		self.setData(data)
		self.setPathSeparator(os.sep)
	# End def

	def setPathSeparator(self, pathSeparator):
		"""Sets the path-separator-string, a key-value is splitted with.
		
		Parameter:
			string pathSeparator Defines the path-separator-string.
		"""
		if type(pathSeparator)!=str: raise TypeError

		self.verbosity.out("setPathSeparator: %s" % pathSeparator);

		self.__pathSeparator=pathSeparator
	# End def

	def getPathSeparator(self):
		"""Returns the path-separator-string."""

		self.verbosity.out("getPathSeparator: %s" % self.__pathSeparator);

		return self.__pathSeparator
	# End def

	def setData(self, data):
		"""Sets the data, see also constructor-method."""

		#self.verbosity.out("setData: %s" % str(data))
		self.verbosity.out("setData")

		if type(data) not in (dict, list, self.__class__, str, int, long, float, bool, type(None), unicode): raise TypeError

		self.__data=data
	# End def

	def getData(self):
		"""Returns the currently used data, see also constructor- and
		setData-methods."""

		self.verbosity.out("getData")

		return self.__data
	# End def

	def typeOfData(self):
		"""Returns the type of the currently used data

		If type(data) is of type "dict" or "__class__" "dir" will be returned,
		type of "list" "list" will be returned, otherwise "file".

		Returns "dir", "list" or "file". See also TYPE_OF_*
		"""

		if type(self.__data) in (dict, self.__class__):
			tod=self.TYPE_OF_DIR
		elif type(self.__data)==list:
			tod=self.TYPE_OF_LIST
		else:
			tod=self.TYPE_OF_FILE
		# End if

		self.verbosity.out("typeOfData: %s" % tod);

		return tod
	# End def

	def __getPathParts(self, key):
		"""Internal method to split the given key-value into parts, defined with
		the path-separator-string.
		
		Parameter:
			string key key-value, like type of "my-key-value" or path-string like,
				for example, "my/key/value".

		Returns:
			Returns a list with splitted key-parts. "Empty" parts will be deleted.
		"""

		self.verbosity.out("__getPathParts: key: %s" % key)

		parts=key.split(self.getPathSeparator())

		if len(parts)>0 and parts[0].strip()=="": del parts[0]
		if len(parts)>0 and parts[-1].strip()=="": del parts[-1]

		return parts
	# End def

	def __getitem__(self, key):
		"""Returns the value of the given key and is combined with the currently stored
		data under that key.

		Example:
			a["b"] => Returns the key-value 'b' of the data.
			a["b/c/d"] => Returns the key-value 'd' of 'c' of 'b' of the data.
			a[0] => Returns the first value, array-element 0, of the data, can be used with
				different types, like dict, list, string, aso.

		Parameter:
			string key key[-path]-string to be returned.
		"""

		self.verbosity.out("__getitem__: key: %s" % key)

		if key==None:
			return self.__data
		# End def
		
		if type(key) in (str, unicode) and self.getPathSeparator() in key:
			key=key.strip(self.getPathSeparator())
			parts=self.__getPathParts(key)

			e="self"
			for partNr in range(0, len(parts)):
				e+=".__getitem__(parts[%i])" % partNr
			# End for

			return eval(e)
		elif type(key)==int and type(self.__data)==dict:
			kv=self.__data.keys()
			if key<0 or key>=len(kv): raise IndexError
			
			tmp=self.__class__()
			tmp.setData(self.__data[kv[key]])
			return tmp
		elif type(self.__data)==list:
			tmp=self.__class__()
			tmp.setData(self.__data[int(key)])
			return tmp
		elif type(self.__data) not in (str, unicode):
			tmp=self.__class__()
			tmp.setData(self.__data[key])
			return tmp
		else:
			tmp=self.__class__()
			tmp.setData(self.__data)
			return tmp
		# End if
	# End def

	def __setitem__(self, key, value):
		"""Sets the value of the given key.

		Example:
			a["b"]=123 => Sets value of the key 'b' to a an integer, 123.
			a["b/c/d"]="Test 456" => Sets the value of the key 'd' of 'c' of 
				'b' to a string, "Test 456".
			a[0]={"boolean": True} => Sets the value of the key '0', first element,
				to a dict-object, {"boolean": True}.

		Parameter:
			string key Defines the key[-path]-value which has to be set.
			mixed value Defines the data-value to be set.
		"""

		self.verbosity.out("__setitem__: key: %s" % key)

		if key==None:
			self.__data=value
			return
		# End if

		if type(key) in (str, unicode) and self.getPathSeparator() in key:
			parts=self.__getPathParts(key)

			e="self"
			for partNr in range(0, len(parts)-1):
				e+=".__getitem__(parts[%i])" % partNr
			# End for
			e+=".__setitem__(parts[%i], value)" % (len(parts)-1)

			eval(e)
		else:
			if type(self.__data)==list and int(key)>=len(self.__data):
				self.__data.append(value)
			else:
				self.__data[int(key) if type(self.__data)==list else key]=value
			# End if
		# End if

	# End def

	def __delitem__(self, key):
		"""Deletes the value and the key of the given key.

		Example:
			del a["b"] => Deletes the value and key under "b".
			del a["b/c/d"] => Deletes the value and key of 'd'.
			del a[0] => Deletes the first element.

		Parameter:
			string key Defines the key[-path]-value which has to be deleted.
		"""

		self.verbosity.out("__delitem__: key: %s" % key)

		if type(key) in (str, unicode) and self.getPathSeparator() in key:
			parts=self.__getPathParts(key)

			e="self"
			for partNr in range(0, len(parts)-1): e+=".__getitem__(parts[%i])" % partNr
			e+=".__delitem__(parts[%i])" % (len(parts)-1)

			eval(e)
		else:
			del self.__data[int(key) if type(self.__data)==list else key]
		# End if
	# End if

	def __len__(self):
		"""Returns the length of the currently stored data."""

		l=len(self.__data)

		self.verbosity.out("__len__: %i" % l)

		return l
	# End def

	def __str__(self):
		s=str(self.__data)

		self.verbosity.out("__str__: %s" % s)

		return s
	# End def

	def has_key(self, key):
		"""Returns true if the given key-path-value is key and so available, false if not.

		Parameter:
			string key Defines the key[-path]-value which has to be checked.
		"""

		self.verbosity.out("has_key: key: %s" % key)

		try:
			self.__getitem__(key)
			self.verbosity.out("has_key: true")
			return True
		except:
			self.verbosity.out("has_key: false")
			return False
		# End try/except
	# End def

	def keys(self, path=""):
		"""Returns a list of type 'list' with the keys of the currently stored data, names
		if type is of type dict or __class__, integer-range if type is of type 'list'.

		Parameter
			string path Defines the key-path-value of which the keys has to be returned.
		"""

		self.verbosity.out("keys: path: %s" % path)

		dataFromPath=self.__getitem__(path) if path else self.__data

		if type(dataFromPath) in (dict, PathDataStorage):
			return dataFromPath.keys()
		elif type(dataFromPath)==list:
			return range(0, len(dataFromPath))
		else:
			raise KeyError
		# End if
	# End def

	def values(self, path=""):
		"""Returns a list of type 'list' with the values of the currently stored data."""

		self.verbosity.out("values: path: %s" % path)

		dataFromPath=self.__getitem__(path) if path else self.__data

		if type(dataFromPath) in (dict, PathDataStorage):
			return dataFromPath.values()
		else:
			return self.__dataFromPath
		# End if
	# End def

# End class
